#include <JSValue.h>

using namespace Impl;

namespace AwesomiumDotNet
{

	/// <summary>
	/// JSValue is a class that represents a Javascript value.
	/// It can be initialized from and converted to several types:
	/// boolean, integer, double, string
	/// </summary>

	public ref class JSValue
	{
	private:
		Object^ value;

	internal:
		JSValue::JSValue(const Awesomium::JSValue& nativeValue)
		{
			if (nativeValue.isBoolean())
				value = nativeValue.toBoolean();
			else if (nativeValue.isInteger())
				value = nativeValue.toInteger();
			else if (nativeValue.isDouble())
				value = nativeValue.toDouble();
			else if (nativeValue.isString())
				value = gcnew String(nativeValue.toString().c_str());
			else if (nativeValue.isNull())
				value = nullptr;
		}

	public:
		/// <summary>Creates a null JSValue.</summary>
		JSValue()
		{
			value = nullptr;
		}

		/// <summary>Creates a JSValue initialized with a boolean.</summary>
		JSValue(bool value)
		{
			this->value = value;
		}

		/// <summary>Creates a JSValue initialized with an integer.</summary>
		JSValue(int value)
		{
			this->value = value;
		}

		/// <summary>Creates a JSValue initialized with a double.</summary>
		JSValue(double value)
		{
			this->value = value;
		}

		/// <summary>Creates a JSValue initialized with a string.</summary>
		JSValue(String^ value)
		{
			this->value = value;
		}

		/// <summary>Returns whether or not this JSValue is a boolean.</summary>
		bool IsBoolean() {
			bool^ tmp = dynamic_cast<bool^>(value);
			return (tmp != nullptr);
		}

		/// <summary>Returns whether or not this JSValue is an integer.</summary>
		bool IsInteger() {
			int^ tmp = dynamic_cast<int^>(value);
			return (tmp != nullptr);
		}

		/// <summary>Returns whether or not this JSValue is a double.</summary>
		bool IsDouble() {
			double^ tmp = dynamic_cast<double^>(value);
			return (tmp != nullptr);
		}

		/// <summary>Returns whether or not this JSValue is a string.</summary>
		bool IsString() {
			String^ tmp = dynamic_cast<String^>(value);
			return (tmp != nullptr);
		}

		/// <summary>Returns whether or not this JSValue is null.</summary>
		bool IsNull() {
			return (value == nullptr);
		}


		/// <summary>Returns this JSValue as a string (converting if necessary).</summary>
		virtual String^ ToString() override
		{
			if (IsNull())
				return "";
			else if (IsBoolean())
				return value->ToString()->ToLower();
			else
				return value->ToString();
		}

		/// <summary>Returns this JSValue as a boolean.</summary>
		bool ToBoolean() {
			return (bool)value;
		}

		/// <summary>Returns this JSValue as an integer.</summary>
		int ToInteger() {
			return (int)value;
		}

		/// <summary>Returns this JSValue as a double.</summary>
		double ToDouble() {
			return (double)value;
		}

	internal:
		Awesomium::JSValue ToNative()
		{
			if (IsNull())
			{
				return Awesomium::JSValue();
			}
			else if (IsInteger())
			{
				return Awesomium::JSValue(ToInteger());
			}
			else if (IsDouble())
			{
				return Awesomium::JSValue(ToDouble());
			}
			else if (IsBoolean())
			{
				return Awesomium::JSValue(ToBoolean());
			}
			else if (IsString())
			{
				std::string valueStr;
				MarshalString(ToString(), valueStr);
				return Awesomium::JSValue(valueStr);
			}
			else
			{
				throw gcnew ArgumentException("Invalid type");
			}
			return Awesomium::JSValue();
		}
	};


	/// <summary>
	/// FutureJSValue is a special wrapper around a JSValue that allows
	/// asynchronous retrieval of the actual value at a later time.
	/// If you are unfamiliar with the concept of a 'Future', please see:
	/// http://en.wikipedia.org/wiki/Futures_and_promises
	/// </summary>

	public ref class FutureJSValue
	{
	private:
		Awesomium::FutureJSValue* futureJSValue;
	
	internal:
		FutureJSValue::FutureJSValue(const Awesomium::FutureJSValue& futureJSValue)
			: futureJSValue(new Awesomium::FutureJSValue(futureJSValue))
		{
		} 
	
	public:
		~FutureJSValue()
		{
			FutureJSValue::!FutureJSValue();
		}

		/// <summary>
		/// If the internal JSValue has been computed, immediately returns
		/// the value, else, blocks the calling thread until it has.
		/// </summary>
		JSValue^ Get()
		{
			return gcnew JSValue(futureJSValue->get());
		}
	
	protected:
		!FutureJSValue()
		{
			delete futureJSValue;
		}
	};
}
